package com.superbit.utils.dbutil;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import javax.transaction.Transactional;

import org.apache.commons.beanutils.BeanUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;


@SuppressWarnings("unchecked")
@Repository(value="commonDao")
public class CommonDao {
	
	public static final int STATISTYPE_COUNT = 0;
	public static final int STATISTYPE_SUM = 1;
	public static final int STATISTYPE_AVG = 2;
	public static final int STATISTYPE_MAX = 3;
	public static final int STATISTYPE_MIN = 4;
	
	public static interface Excutor{//内部接口，用于 使用session来完成更复杂的数据操作
		Object excute(Session session);
	}
	
	@Autowired
	private SessionFactory sessionFactory;
	
	private Session getQuerySession(){
		try{
			return sessionFactory.getCurrentSession();
		}catch(Exception e){
			//不在事务中时，返回openSession
			return sessionFactory.openSession();
		}
	}
	
	/**复杂数据库操作，统一使用Excutor模式，可有效避免忘记加入事务，导致连接不被释放的风险
	 * @param excutor
	 * @return
	 */
	@Transactional
	public Object excute(Excutor excutor){
		Session session = this.getQuerySession();
		Object obj = excutor.excute(session);
		return obj;
	}
	
	
	/**保存实体
	 * @param obj
	 * @return
	 */
	@Transactional
	public Serializable saveEntry(Object obj){
		Session session = this.getQuerySession();
		//执行操作
		Serializable id = session.save(obj);
		return id;
	}
	
	/**删除实体
	 * @param cls
	 * @param id
	 */
	@Transactional
	public void deleteEntry(Class<?> cls,Serializable id){
		Session session = this.getQuerySession();
		//执行操作
		Object obj = session.get(cls,id);
		if(obj!=null){			
			session.delete(obj);
		}
	}
	
	/**更新实体
	 * @param obj
	 */
	@Transactional
	public void updateEntry(Object obj){
		Session session = this.getQuerySession();
		session.update(obj);
	}
	
	/** 更新多个属性
	 * @param updateInfo
	 * @param id
	 */
	@Transactional
	public void updateEntry(Class<?> cls,Map<String,Object> updateInfo,Serializable id){
		Session session = this.getQuerySession();
		//执行操作
		Assert.notNull(id);
		Object obj = session.get(cls,id);
		if(obj!=null){			
			try {
				BeanUtils.copyProperties(obj, updateInfo);
			} catch (Exception e) {
				throw new RuntimeException("参数有误，无法修改相关属性");
			} 
			session.save(obj);
		}
	}
	
	/**更新实体
	 * @param cls
	 * @param updateInfo
	 * @param condition
	 */
	@Transactional
	public void updateEntrys(Class<?> cls,Map<String,Object> updateInfo,String condition){
		Session session = this.getQuerySession();
		//执行操作
		if(condition==null) condition= "1=0";
		if(condition.indexOf(";")>=0){
			throw new RuntimeException("condition params error");
		}
		if(updateInfo==null||updateInfo.size()==0){
			throw new RuntimeException("缺少更新字段");
		}
		StringBuffer sb = new StringBuffer();
		sb.append("update "+cls.getSimpleName()+" set ");
		for(String key : updateInfo.keySet()){
			sb.append(key+"=:"+key+",");
		}
		sb.deleteCharAt(sb.length()-1);
		sb.append(" where "+condition);
		Query query = session.createQuery(sb.toString());
		
		for(String key : updateInfo.keySet()){
			Object value = updateInfo.get(key);
			query.setParameter(key, value);
		}
		query.executeUpdate();
	}
	
	/**删除实体
	 * @param cls
	 * @param condition
	 */
	@Transactional
	public void deleteEntrys(Class<?> cls,String condition){
		Session session = this.getQuerySession();
		//执行操作
		if(condition==null) condition= "1=0";
		if(condition.indexOf(";")>=0){
			throw new RuntimeException("condition params error");
		}
		StringBuffer sb = new StringBuffer();
		sb.append("delete from  "+cls.getSimpleName()+" ");
		sb.append(" where "+condition);
		session.createQuery(sb.toString()).executeUpdate();
		
	}
	
	/**获取实体
	 * @param id
	 * @param cls
	 * @return
	 */
	@Transactional
	public <T> T getEntry(Serializable id, Class<T> cls) {
		return (T)this.getQuerySession().get(cls, id);
	}
	
	/**根据属性获取实体列表
	 * @param cls
	 * @param conditions
	 * @param orders
	 * @return
	 */
	@Transactional
	public <T> List<T> getEntrysByField (Class<T> cls, Map<String,Object> fieldMap,String[] orders){
		Session session = this.getQuerySession();
		Criteria crit = session.createCriteria(cls);
		if(fieldMap!=null){
			for(String cond : fieldMap.keySet()){				
				crit.add(Restrictions.eq(cond, fieldMap.get(cond)));
			}
		}
		if(orders!=null){
			for(String order : orders){
				if(order!=null){
					order = order.trim();
					if(order.indexOf(" ")>=0){
						String[] words = order.split(" ");
						if(words.length!=2){
							throw new RuntimeException("排序格式不正确："+order);
						}
						String name  = words[0];
						String direct = words[1];
						if(direct.equalsIgnoreCase("ASC")){
							crit.addOrder(Order.asc(name));
						}else if(direct.equalsIgnoreCase("DESC")){
							crit.addOrder(Order.desc(name));
						}else{
							throw new RuntimeException("排序格式不正确："+order);
						}
					}else{						
						crit.addOrder(Order.asc(order));
					}
				}
			}
		}
		return (List<T>)crit.list();
	}
	
	/**根据属性获取实体列表
	 * @param cls
	 * @param fieldMap
	 * @param orders
	 * @param start
	 * @param size
	 * @return
	 */
	@Transactional
	public <T> List<T> getEntrysByField(Class<T> cls, Map<String,Object> fieldMap,String[] orders,int start ,int size){
		Session session = this.getQuerySession();
		Criteria crit = session.createCriteria(cls);
		if(fieldMap!=null){
			for(String cond : fieldMap.keySet()){				
				crit.add(Restrictions.eq(cond, fieldMap.get(cond)));
			}
		}
		if(orders!=null){
			for(String order : orders){
				if(order!=null){
					order = order.trim();
					if(order.indexOf(" ")>=0){
						String[] words = order.split(" ");
						if(words.length!=2){
							throw new RuntimeException("排序格式不正确："+order);
						}
						String name  = words[0];
						String direct = words[1];
						if(direct.equalsIgnoreCase("ASC")){
							crit.addOrder(Order.asc(name));
						}else if(direct.equalsIgnoreCase("DESC")){
							crit.addOrder(Order.desc(name));
						}else{
							throw new RuntimeException("排序格式不正确："+order);
						}
					}else{						
						crit.addOrder(Order.asc(order));
					}
				}
			}
		}
		crit.setFirstResult(start);
		crit.setMaxResults(size);
		return (List<T>)crit.list();
	}
	
	/**根据条件获取实体列表
	 * @param cls
	 * @param conditions
	 * @param orders
	 * @return
	 */
	@Transactional
	public <T> List<T> getEntrysByCond(Class<T> cls,Criterion[] conditions,Order[] orders){
		Session session = this.getQuerySession();
		Criteria crit = session.createCriteria(cls);
		if(conditions!=null){
			for(Criterion cond : conditions){				
				crit.add(cond);
			}
		}
		
		if(orders!=null){
			for(Order order : orders){				
				crit.addOrder(order);
			}
		}
		return (List<T>)crit.list();
	}
	
	/**根据条件获取实体列表
	 * @param cls
	 * @param conditions
	 * @param orders
	 * @param start
	 * @param size
	 * @return
	 */
	@Transactional
	public <T> List<T> getEntrysByCond(Class<T> cls,Criterion[] conditions,Order[] orders,int start,int size){
		Session session = this.getQuerySession();
		Criteria crit = session.createCriteria(cls);
		if(conditions!=null){
			for(Criterion cond : conditions){				
				crit.add(cond);
			}
		}
		
		if(orders!=null){
			for(Order order : orders){				
				crit.addOrder(order);
			}
		}
		crit.setFirstResult(start);
		crit.setMaxResults(size);
		return (List<T>)crit.list();
	}
	
	/**
	 * @function 根据条件获取实体列表
	 * @param cls
	 * @param conditions
	 * @param orders
	 * @param start
	 * @param size
	 * @return
	 */
	@Transactional
	public <T> List<T> getEntrysByCond(Class<T> cls,List<Criterion> conditions,String[] orders,int start,int size){
		Session session = this.getQuerySession();
		Criteria crit = session.createCriteria(cls);
		if(conditions!=null){
			for(Criterion cond : conditions){				
				crit.add(cond);
			}
		}
		
		if(orders!=null){
			for(String order : orders){
				if(order!=null){
					order = order.trim();
					if(order.indexOf(" ")>=0){
						String[] words = order.split(" ");
						if(words.length!=2){
							throw new RuntimeException("排序格式不正确："+order);
						}
						String name  = words[0];
						String direct = words[1];
						if(direct.equalsIgnoreCase("ASC")){
							crit.addOrder(Order.asc(name));
						}else if(direct.equalsIgnoreCase("DESC")){
							crit.addOrder(Order.desc(name));
						}else{
							throw new RuntimeException("排序格式不正确："+order);
						}
					}else{						
						crit.addOrder(Order.asc(order));
					}
				}
			}
		}
		crit.setFirstResult(start);
		crit.setMaxResults(size);
		return (List<T>)crit.list();
	}
	
	/**
	 * @function 根据条件获取数量
	 * @param cls
	 * @param conditions
	 * @return
	 */
	@Transactional
	public int getCount(Class<?> cls,List<Criterion> conditions){
		Session session = this.getQuerySession();
		Criteria crit = session.createCriteria(cls);
		if(conditions!=null){
			for(Criterion cond : conditions){				
				crit.add(cond);
			}
		}
		crit.setProjection(Projections.rowCount());
		List<Long> list = crit.list();
		if(list==null||list.size()==0){
			return 0;
		}else{
			Long value = list.get(0);
			if(value==null){
				return 0;
			}else{
				return (int)(long)value;
			}
		}
	}
	
	/**
	 * @function 获取统计结果，只能统计单个字段
	 * @param cls
	 * @param conditions
	 * @param statisType
	 * @param fieldName
	 * @return
	 */
	@Transactional
	public Object getStatisResult(Class<?> cls,List<Criterion> conditions,int statisType,String fieldName){
		if(statisType<0||statisType>4){
			throw new RuntimeException("staticType not in Scope");
		}
		Session session = this.getQuerySession();
		Criteria crit = session.createCriteria(cls);
		if(conditions!=null){
			for(Criterion cond : conditions){				
				crit.add(cond);
			}
		}
		if(statisType==STATISTYPE_COUNT){			
			crit.setProjection(Projections.rowCount());
		}else if(statisType==STATISTYPE_AVG){
			crit.setProjection(Projections.avg(fieldName));
		}else if(statisType==STATISTYPE_SUM){
			crit.setProjection(Projections.sum(fieldName));
		}else if(statisType==STATISTYPE_MAX){
			crit.setProjection(Projections.max(fieldName));
		}else if(statisType==STATISTYPE_MIN){
			crit.setProjection(Projections.min(fieldName));
		}
		List<Object> list = crit.list();
		if(list==null||list.size()==0){
			if(statisType<3){
				return 0;
			}else{				
				return null;
			}
		}else{
			Object value = list.get(0);
			if(value==null){
				if(statisType<3){
					return 0;
				}else{				
					return null;
				}
			}else{
				return value;
			}
		}
	}
	
	/**
	 * @function 根据条件获取数量
	 * @param cls
	 * @param conditions
	 * @return
	 */
	@Transactional
	public int getCount(Class<?> cls,Criterion[] conditions){
		Session session = this.getQuerySession();
		Criteria crit = session.createCriteria(cls);
		if(conditions!=null){
			for(Criterion cond : conditions){				
				crit.add(cond);
			}
		}
		crit.setProjection(Projections.rowCount());
		List<Long> list = crit.list();
		if(list==null||list.size()==0){
			return 0;
		}else{
			Long value = list.get(0);
			if(value==null){
				return 0;
			}else{
				return (int)(long)value;
			}
		}
	}
	
	
	/**
	 * @function 根据条件获取数量
	 * @param cls
	 * @param fieldMap
	 * @return
	 */
	@Transactional
	public int getCount(Class<?> cls,Map<String,Object> fieldMap){
		Session session = this.getQuerySession();
		Criteria crit = session.createCriteria(cls);
		if(fieldMap!=null){
			for(String cond : fieldMap.keySet()){				
				crit.add(Restrictions.eq(cond, fieldMap.get(cond)));
			}
		}
		crit.setProjection(Projections.rowCount());
		List<Long> list = crit.list();
		if(list==null||list.size()==0){
			return 0;
		}else{
			Long value = list.get(0);
			if(value==null){
				return 0;
			}else{
				return (int)(long)value;
			}
		}
	}
}